package com.example.gobang.controller;

import com.example.gobang.game.MatchRequest;
import com.example.gobang.game.MatchResponse;
import com.example.gobang.game.Matcher;
import com.example.gobang.game.OnlineUserManager;
import com.example.gobang.model.UserInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

/**
 * Description: 通过这个类来处理匹配功能中的 WebSocket 请求
 * Date: 2024-09-05
 * Time: 10:19
 */
@Configuration
public class MatchController extends TextWebSocketHandler {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private Matcher matcher;


    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        /**
         * WebSocketSession 的 getAttributes 方法可以获取与当前 WebSocket 会话相关的属性集合
         * 如果当前用户没有通过 HTTP 来登录，而是直接通过 /game_hall.html 来访问游戏大厅，那么此时 userInfo 就为空
         * 所以需要捕获空指针异常
         */

        try {
            /**
             * 玩家上线，加入到 OnlineUserManager 中
             * 1.先获取当前用户的身份信息
             * 在注册 WebSocket 时加上的拦截器把 HttpSession 中的 Attribute 都拿到 WebSocket 中
             * 而在 Http 的登录逻辑中，我们往 HttpSession 中存放了 UserInfo 的数据：httpSession.setAttribute("userInfo",userInfo);
             * 所以可以在 WebSocketSession 中拿到之前在 HttpSession 中存放的 User 对象
             */
            UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");

            // 2.判断当前用户是否已登录（处于在线状态），如果是，那就不该执行后续逻辑
            if(onlineUserManager.getFromGameHall(userInfo.getUserId()) != null
                    || onlineUserManager.getFromGameRoom(userInfo.getUserId()) != null) {
                MatchResponse matchResponse = new MatchResponse();
                matchResponse.setOk(true);
                matchResponse.setReason("当前账号已登录");
                matchResponse.setMessage("repeatConnection");
                session.sendMessage(new TextMessage(objectMapper.writeValueAsString(matchResponse)));
                // session.close();
                return;
            }

            // 3.拿到身份信息之后就可以把玩家设置为在线状态
            onlineUserManager.enterGameHall(userInfo.getUserId(), session);
            System.out.println("玩家 " + userInfo.getUserName() + " 进入游戏大厅");
        } catch (NullPointerException e) {
            /**
             * 出现空指针异常说明当前用户未登录
             * 需要把当前用户未登录这个信息返回
              */
            e.printStackTrace();
            MatchResponse response = new MatchResponse();
            response.setOk(false);
            /**
             * 先通过 ObjectMapper 将 MatchResponse 对象转为 JSON 字符串
             * 然后再包装上一层 TextMessage 进行传输
             * TextMessage 表示一个文本格式的 WebSocket 数据包
             */
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }

    }

    /**
     * 处理开始匹配请求和结束匹配请求
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");
        // 获取客户端给服务器发送的数据
        String payload = message.getPayload();
        // 当前 payload 是一个 JSON 格式的字符串，需要把它转成 Java 对象，即 MatchRequest
        MatchRequest request = objectMapper.readValue(payload,MatchRequest.class);
        MatchResponse response = new MatchResponse();
        if (request.getMessage().equals("startMatch")) {
            // 将玩家加入匹配队列
            matcher.add(userInfo);
            // 把玩家信息放进匹配队列之后，返回一个响应给客户端
            response.setOk(true);
            response.setMessage("startMatch");
        } else if (request.getMessage().equals("stopMatch")) {
            // 将玩家从匹配队列中移除
            matcher.remove(userInfo);
            // 移除后返回响应给客户端
            response.setOk(true);
            response.setMessage("stopMatch");
        } else {
            // 非法情况
            response.setOk(false);
            response.setReason("非法的匹配请求");
        }
        String jsonString = objectMapper.writeValueAsString(response);
        session.sendMessage(new TextMessage(jsonString));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {super.handleTransportError(session, exception);
        try {
            // 连接异常，玩家下线，从 OnlineUserManager 中删除
            UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");
            // 参数部分的 session 是当前登录的 session，下面的 tmpSession 是已登录的 session
            onlineUserManager.exitGameHall(userInfo.getUserId());

            // 如果玩家正在匹配，但是 WebSocket 连接断开了，那就应该把他移出匹配队列
            matcher.remove(userInfo);

        } catch (NullPointerException e) {
            MatchResponse response = new MatchResponse();
            response.setOk(false);
            response.setReason("您尚未登录，请登录后再匹配");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }



    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {super.afterConnectionClosed(session, status);
        try {
            // 玩家下线，从 OnlineUserManager 中删除
            UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");
            WebSocketSession tmpSession = onlineUserManager.getFromGameHall(userInfo.getUserId());
            // 我们需要检查这两个 session 是否一样
            // 如果一样，说明想关闭 WebSocket 连接的是正常上号的 session，此时可以关
            // 如果不一样，说明就是已经登录，然后想要多开，此时这里不能退出，不然会把原先那个号从哈希表里移除
            if (session == tmpSession) {
                onlineUserManager.exitGameHall(userInfo.getUserId());
            }

            // 如果玩家正在匹配，但是 WebSocket 连接断开了，那就应该把他移出匹配队列
            matcher.remove(userInfo);

        } catch (NullPointerException e) {
            MatchResponse response = new MatchResponse();
            response.setOk(false);
            response.setReason("您尚未登录，请登录后再匹配");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
    }
}
